package pagecal

import (
	"bytes"
	"context"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"magGo/src/db"
	"math"
	"net/http"
	"os"
	"sort"
	"strconv"
	"sync"
	"time"

	"go.mongodb.org/mongo-driver/bson"
	"go.mongodb.org/mongo-driver/mongo"
	"go.mongodb.org/mongo-driver/mongo/options"
)

var (
	AllSubjects []string
	PubMap      sync.Map
)

func init() {
	AllSubjects = []string{"Number theory", "Logic", "Genome editing", "Quantum computing", "Experimental physics", "Deep learning", "Operating system", "Genetic engineering", "Literature", "Geometry", "Industrial engineering", "Applied physics", "Applied mathematics", "Algebra", "Cognitive science", "Theoretical physics", "Philosophy", "Linguistics", "Biological engineering", "Anthropology", "Computer engineering", "Civil engineering", "History", "Theoretical computer science", "Discrete mathematics", "Political science", "Environmental engineering", "Mechanical engineering", "Earth science", "Geography", "Machine learning", "Electrical engineering", "Chemical engineering", "Environmental science", "Sociology", "Neuroscience", "Artificial intelligence", "Geology", "Economics", "Mathematics", "Physics", "Psychology", "Materials science", "Computer science", "Engineering disciplines", "Chemistry", "Biology", "Medicine"}
	// AllSubjects = []string{"Number theory", "Civil engineering", "Theoretical computer science", "Discrete mathematics", "Political science", "Environmental engineering", "Mechanical engineering", "Earth science", "Geography", "Machine learning", "Electrical engineering", "Chemical engineering", "Environmental science", "Sociology", "Neuroscience", "Artificial intelligence", "Geology", "Economics", "Mathematics", "Physics", "Psychology", "Materials science", "Computer science", "Engineering disciplines", "Chemistry", "Biology", "Medicine"}
	PubMap = sync.Map{}
}

type DisruptionData struct {
	Ic int `bson:"ic"`
	I  int `bson:"i"`
	J  int `bson:"j"`
	K  int `bson:"k"`
	Ac int `bson:"ac"`
	Oc int `bson:"oc"`
}

func (c *DisruptionData) GetD() float64 {
	// 补丁
	k := c.K
	if c.K > 0 {
		k -= 1
	}
	return (float64(c.I) - float64(c.J)) / (float64(c.I) + float64(c.J) + float64(k))
}

type Fields struct {
	Id   string `bson:"_id"`
	D    DisruptionData
	In   []string `bson:"in"`
	Out  []string `bson:"out"`
	S    []string `bson:"S"`
	Year int      `bson:"year"`
}

// 统计学科的 sD，
// todo 需要一个公共的本地 node map
func StatisticsAllSubject() {
	wg := sync.WaitGroup{}
	// todo
	wg.Add(len(AllSubjects))
	for _, name := range AllSubjects {
		go func(name string) {
			// 存储 D
			DS := []Fields{}
			ctx := context.TODO()
			collection := db.Client.Database("mag").Collection("pageinfo")
			RetCollection := db.Client.Database("mag").Collection("trendPageDSubject")
			cur, err := collection.Find(ctx, bson.M{"S": name})
			if err != nil {
				log.Fatal(err)
			}
			defer cur.Close(ctx)
			for cur.Next(ctx) {
				var result Fields
				err := cur.Decode(&result)
				if err != nil {
					log.Fatal(err)
				}
				// log.Println(result)
				DS = append(DS, result)
			}
			if err := cur.Err(); err != nil {
				log.Fatal(err)
			}
			// 降序
			sort.Slice(DS, func(i, j int) bool {
				return DS[i].D.GetD() > DS[j].D.GetD()
			})
			// 计算5%-100%，set:5%
			// log.Println(DS)
			for i := 5; i <= 100; i += 5 {

				// 跳过已完成的任务
				var result bson.M
				err := RetCollection.FindOne(context.Background(), bson.M{"_id": fmt.Sprintf("%s_%d", name, i)}).Decode(&result)
				if err == nil {
					log.Println("finsh", fmt.Sprintf("%s_%d", name, i))
					continue
				}

				endSlice := int(float64(len(DS)) / 100 * float64(i))
				var ave float64
				inmap := make(map[string]int)
				outmap := make(map[string]int)
				for _, v := range DS[0:endSlice] {
					ave += v.D.GetD()
					for _, in_Id := range v.In {
						// var inObj Fields
						// collection.FindOne(context.Background(), bson.M{"_id": in_Id}).Decode(&inObj)
						inObj := GetField(in_Id, collection)
						// 判断不为空
						if inObj.Id != "" {
							for _, subject := range inObj.S {
								inmap[subject] += 1
							}
							if len(inObj.S) == 0 {
								inmap["empty"] += 1
							}
						}
					}
					for _, in_Id := range v.Out {
						// var outObj Fields
						// collection.FindOne(context.Background(), bson.M{"_id": in_Id}).Decode(&outObj)
						outObj := GetField(in_Id, collection)
						if outObj.Id != "" {
							for _, subject := range outObj.S {
								outmap[subject] += 1
							}
							if len(outObj.S) == 0 {
								outmap["empty"] += 1
							}
						}
					}
				}
				ave = ave / float64(endSlice)
				// log.Println(ave)
				RetCollection.InsertOne(ctx, bson.M{"_id": fmt.Sprintf("%s_%d", name, i), "subject": name, "average": ave, "percent": i, "outS": outmap, "inS": inmap})
			}
			log.Println(name, " Done")
			wg.Done()
		}(name)
	}
	wg.Wait()
}

// 公共缓存 field
func GetField(_id string, collection *mongo.Collection) (outObj Fields) {
	if v, ok := PubMap.Load(_id); ok {
		outObj = v.(Fields)
	} else {
		collection.FindOne(context.Background(), bson.M{"_id": _id}).Decode(&outObj)
		PubMap.Store(_id, outObj)
	}
	return
}

type powlerOut struct {
	Subject string
	Data    []float64
}

// 统计学科的 sD 幂律，分布，数据存储到本地文件
func PowerLowAllSubject() {
	wg := sync.WaitGroup{}
	// todo
	wg.Add(len(AllSubjects))

	chanout := make(chan powlerOut, 100)
	// 数据存储端
	go func() {
		dumpData := make(map[string][]float64)
		for obj := range chanout {
			dumpData[obj.Subject] = obj.Data
		}
		filePath := "/tmp/mag_ret.json"
		file, err := os.OpenFile(filePath, os.O_CREATE|os.O_RDWR, 0664)
		if err != nil {
			panic(err)
		}
		defer file.Close()
		b, err := json.Marshal(dumpData)
		_, err = file.Write(b)
		fmt.Println(err)
	}()

	for _, name := range AllSubjects {
		go func(name string) {
			// 存储 D
			DS := []Fields{}
			ctx := context.TODO()
			collection := db.Client.Database("mag").Collection("pageinfo")
			cur, err := collection.Find(ctx, bson.M{"S": name}, options.Find().SetProjection(bson.M{"D": 1}))
			if err != nil {
				log.Fatal(err)
			}
			defer cur.Close(ctx)
			for cur.Next(ctx) {
				var result Fields
				err := cur.Decode(&result)
				if err != nil {
					log.Fatal(err)
				}
				// log.Println(result)
				DS = append(DS, result)
			}
			if err := cur.Err(); err != nil {
				log.Fatal(err)
			}
			// 降序
			sort.Slice(DS, func(i, j int) bool {
				return DS[i].D.GetD() > DS[j].D.GetD()
			})

			retLogArray := []float64{}
			retDArray := []float64{}
			for i, item := range DS {
				if i >= 100000 {
					break
				}
				retDArray = append(retDArray, item.D.GetD())
				if item.D.GetD() <= 0 {
					break
				}
				retLogArray = append(retLogArray, math.Log10(item.D.GetD()))
			}
			postObj := make(map[string]interface{})
			postObj["path"] = "Disruption_page_" + name
			postObj["data"] = retDArray
			ret := Post("http://192.168.1.226:8080/goapi/storage/create", postObj, "application/json")
			log.Println(ret)

			postObj = make(map[string]interface{})
			postObj["path"] = "Disruption_PowerLow_" + name
			postObj["data"] = retLogArray
			ret = Post("http://192.168.1.226:8080/goapi/storage/create", postObj, "application/json")
			log.Println(ret)

			chanout <- powlerOut{Subject: name, Data: retLogArray}
			// 输出 幂律D
			log.Println(name, " Done")
			wg.Done()
		}(name)
	}
	wg.Wait()
	close(chanout)
	<-time.After(2 * time.Second)
	log.Println("over")

}

// 统计学科的 sD 幂律，分布，按年，按量分布，
// 1945-2020 每年按 5% 的 top 学科，或者固定数量继续计算
func PowerLowAllSubjectByYear() {
	wg := sync.WaitGroup{}
	// todo
	wg.Add(len(AllSubjects))

	for _, name := range AllSubjects {
		go func(name string) {
			// 按年保存的
			YearFields := make(map[int][]Fields)
			// 存储 D
			// DS := []Fields{}
			ctx := context.TODO()
			collection := db.Client.Database("mag").Collection("pageinfo")
			cur, err := collection.Find(ctx, bson.M{"S": name})
			if err != nil {
				log.Fatal(err)
			}
			defer cur.Close(ctx)
			for cur.Next(ctx) {
				var result Fields
				err := cur.Decode(&result)
				if err != nil {
					log.Fatal(err)
				}
				// log.Println(result)
				if result.Year != 0 {
					YearFields[result.Year] = append(YearFields[result.Year], result)
				}
			}
			if err := cur.Err(); err != nil {
				log.Fatal(err)
			}
			// 每年降序
			for _, item := range YearFields {
				sort.Slice(item, func(i, j int) bool {
					return item[i].D.GetD() > item[j].D.GetD()
				})
			}

			type outdata struct {
				Year  int       `json:"year"`
				Count int       `json:"count"`
				Data  []float64 `json:"data"`
			}

			subject_ret_data := []outdata{}
			// 按年遍历 1945~2020
			for year := 1945; year < 2020; year++ {
				od := outdata{Count: len(YearFields[year]), Year: year}
				if len(YearFields[year]) == 0 {
					continue
				}
				for i := 5; i <= 100; i += 5 {
					endSlice := int(float64(len(YearFields[year])) / 100 * float64(i))
					if endSlice == 0 && len(YearFields[year]) > 0 {
						endSlice = 1
					}
					var ave float64
					for _, v := range YearFields[year][0:endSlice] {
						ave += v.D.GetD()
					}
					ave = ave / float64(endSlice)
					od.Data = append(od.Data, FloatRound(ave, 6))
				}
				subject_ret_data = append(subject_ret_data, od)
			}

			postObj := make(map[string]interface{})
			postObj["path"] = "Disruption_page_by_year_" + name
			postObj["data"] = subject_ret_data
			ret := Post("http://192.168.1.226:8080/goapi/storage/create", postObj, "application/json")
			// log.Println(subject_ret_data)
			log.Println(ret)

			// 输出 幂律D
			log.Println(name, " Done")
			wg.Done()
		}(name)
	}
	wg.Wait()
	<-time.After(2 * time.Second)
	log.Println("over")

}

func FloatRound(f float64, n int) float64 {
	format := "%." + strconv.Itoa(n) + "f"
	res, _ := strconv.ParseFloat(fmt.Sprintf(format, f), 64)
	return res
}

func Post(url string, data interface{}, contentType string) string {

	// 超时时间：5秒
	client := &http.Client{Timeout: 5 * time.Second}
	jsonStr, _ := json.Marshal(data)
	resp, err := client.Post(url, contentType, bytes.NewBuffer(jsonStr))
	if err != nil {
		panic(err)
	}
	defer resp.Body.Close()

	result, _ := ioutil.ReadAll(resp.Body)
	return string(result)
}
